package com.lll.simple.rpc.provider;

import com.lll.simple.rpc.anno.RpcService;
import com.lll.simple.rpc.common.RpcServiceHelper;
import com.lll.simple.rpc.common.ServiceMeta;
import com.lll.simple.rpc.protocol.decoder.RpcDecoder;
import com.lll.simple.rpc.protocol.encoder.RpcEncoder;
import com.lll.simple.rpc.protocol.handler.RpcIdleStateHandler;
import com.lll.simple.rpc.protocol.handler.RpcRequestHandler;
import com.lll.simple.rpc.registry.RegistryService;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanPostProcessor;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author: liull
 * @Description:
 * @Date: Created in 17:05 2021/1/31
 * @Modified By:
 */
@Slf4j
public class RpcProvider implements InitializingBean, BeanPostProcessor {

    private String serverAddress;

    private final int serverPort;

    private final RegistryService registryService;

    private final Map<String, Object> rpcServiceMap = new HashMap<>();

    public RpcProvider(int serverPort, RegistryService registryService) {
        this.serverPort = serverPort;
        this.registryService = registryService;
    }

    @Override
    public void afterPropertiesSet() throws UnknownHostException {
        this.serverAddress = InetAddress.getLocalHost().getHostAddress();
        new Thread(() -> {
            try {
                startRpcServer();
            } catch (Exception e) {
                log.error("", e);
            }
        }).start();
    }

    /**
     * afterPropertiesSet执行之后执行本方法
     * 
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        RpcService rpcService = bean.getClass().getAnnotation(RpcService.class);
        if (rpcService != null) {
            String serviceName = rpcService.serviceInterface().getName();
            String serviceVersion = rpcService.serviceVersion();
            try {
                ServiceMeta serviceMeta = new ServiceMeta();
                serviceMeta.setServiceAddr(serverAddress);
                serviceMeta.setServicePort(serverPort);
                serviceMeta.setServiceName(serviceName);
                serviceMeta.setServiceVersion(serviceVersion);
                // 发布元数据至注册中心
                registryService.register(serviceMeta);
                rpcServiceMap.put(RpcServiceHelper.buildServiceKey(serviceMeta.getServiceName(), serviceMeta.getServiceVersion()), bean);
            } catch (Exception e) {
                log.error("failed to register service {}#{}", serviceName, serviceVersion, e);
            }
        }
        return bean;
    }

    private void startRpcServer() throws Exception {
        // EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        // EventLoopGroup workerGroup = new NioEventLoopGroup();
        // ServerBootstrap serverBootstrap = new ServerBootstrap();
        // // this.serverAddress = InetAddress.getLocalHost().getHostAddress();
        // try {
        // ChannelFuture channelFuture = serverBootstrap.group(bossGroup, workerGroup)
        // .channel(NioServerSocketChannel.class)
        // // .childOption(ChannelOption.SO_BACKLOG, 1024)
        // // .childOption(ChannelOption.TCP_NODELAY, true)
        // .childOption(ChannelOption.SO_KEEPALIVE, true)
        // .childHandler(new ChannelInitializer<SocketChannel>() {
        // @Override
        // protected void initChannel(SocketChannel ch) throws Exception {
        // ChannelPipeline pipeline = ch.pipeline();
        // pipeline.addLast(new RpcEncoder());
        // pipeline.addLast(new RpcDecoder());
        // pipeline.addLast(new RpcRequestHandler(rpcServiceMap));
        // // pipeline.addLast("responseHandler", new RpcResponseHandler());
        // }
        // }).bind(serverAddress, serverPort).sync();
        // log.info("server addr {} started on port {}", this.serverAddress, this.serverPort);
        // channelFuture.channel().closeFuture().sync();
        // } finally {
        // bossGroup.shutdownGracefully();
        // workerGroup.shutdownGracefully();
        // }
        EventLoopGroup boss = new NioEventLoopGroup();
        EventLoopGroup worker = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(boss, worker)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline()
                                    .addLast(new RpcIdleStateHandler())
                                    .addLast(new RpcEncoder())
                                    .addLast(new RpcDecoder())
                                    .addLast(new RpcRequestHandler(rpcServiceMap));
                        }
                    })
                    .childOption(ChannelOption.SO_KEEPALIVE, true);

            ChannelFuture channelFuture = bootstrap.bind(this.serverAddress, this.serverPort).sync();
            log.info("server addr {} started on port {}", this.serverAddress, this.serverPort);
            channelFuture.channel().closeFuture().sync();
        } finally {
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }
    }
}
